home *** CD-ROM | disk | FTP | other *** search
- /*
- File: VU-Meter.c
-
- Contains: Sample pre-mixer sound component
-
- Written by: Mark Cookson
-
- Copyright: Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
- 8/20/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
-
- #include <Memory.h>
- #include <Errors.h>
- #include <SoundInput.h>
- #include <Components.h>
- #include <Gestalt.h>
- #include <Sound.h>
-
- #include "VU-Meter.h"
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Constants
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Component Dispatcher
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- #define SoundComponentEntryPoint main
-
- #include "ComponentDispatch.c"
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // prototypes
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- static void VUMeterBuffer(SoundComponentGlobalsPtr globals, const Byte * const inputBuffer, long samples, short sampleSize, short numChannels);
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Component Manager Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- /* ==============================================================================
- Component Open
-
- This routine is called when the Component Manager creates an instance of this
- component. The routine should allocate global variables in the appropriate heap
- and call SetComponentInstanceStorage() so the Component Manager can remember
- the globals and pass them to all the method calls.
-
- Determining the heap to use can be tricky. The Component Manager will normally
- load the component code into the system heap, which is good, since many applications
- will be sharing this component to play sound. In this case, the components's global
- variable storage should also be created in the system heap.
-
- However, if system heap memory is tight, the Component Manager will load
- the component into the application heap of the first application that plays sound.
- When this happens, the component should create global storage in the application heap
- instead. The Sound Manager will make sure that other applications will not try
- to play sound while the component is in this application heap.
-
- To determine the proper heap to use, call GetComponentInstanceA5(). If the value
- returned is 0, then the component was loaded into the system heap, and all storage
- should be allocated there. If the value returned is non-zero, the component is in
- the application heap specifed by returned A5 value, and all storage should be
- allocated in this application heap.
-
- NOTE: If the component is loaded into the application heap, the value returned by
- GetComponentRefCon() will be 0.
- NOTE: Do not attempt to initialize in this call, since the Component Manager will
- call Open() BEFORE calling Register().
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentOpen(void *unused1, ComponentInstance self) {
- #pragma unused (unused1)
-
- Handle h;
- SoundComponentGlobalsPtr globals;
-
- h = NewHandleClear(sizeof(SoundComponentGlobals)); // get space for globals
- if (h == nil)
- return(MemError());
-
- HLock(h);
- globals = (SoundComponentGlobalsPtr) *h;
- SetComponentInstanceStorage (self, (Handle) globals); // save pointer to our globals
-
- globals->globalsHandle = h; // remember the handle
-
- return (noErr);
- }
-
- /* ==============================================================================
- Component Close
-
- This routine is called when the Component Manager is closing the instance of
- this component. It should delete all global storage and close any other components
- that were opened.
-
- NOTE: Be sure to check that the globals pointer passed in to this routine is
- not set to NIL. If the Open() routine fails for any reason, the Component
- Manager will call this routine passing in a NIL for the globals.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentClose(SoundComponentGlobalsPtr globals, ComponentInstance self) {
- #pragma unused (self)
-
- if (globals) { // we have some globals
- if (globals->sourceComponent) // we have a source component
- CloseComponent(globals->sourceComponent); // close it
-
- DisposeHandle(globals->globalsHandle); // dispose our storage
- }
-
- return (noErr);
- }
-
- /* ==============================================================================
- Component Register
-
- This routine is called once, usually at boot time, when the Component Manager
- is first registering this sound component. This routine should check to see if the proper
- Sound Manager is installed and return 0 if it is. If the right Sound Manager is not
- installed, the routine should return 1 and this component will not be registered.
-
- NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
- sound component in order for this routine to be called.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentRegister(SoundComponentGlobalsPtr globals)
- {
- #pragma unused (globals)
-
- NumVersionVariant version;
-
- version.parts = SndSoundManagerVersion(); // get the Sound Manager version
- if (version.whole < 0x03210000) // it's what we need
- return (1); // do not install component
- else
- return (0); // install this component
- }
-
- /* ==============================================================================
- Component GetInfo
-
- This is called when a program issues a SndGetInfo() call. If we see our selector
- we return the peak value of the last buffer, if it's anything else we forward
- the selector to our source component.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentGetInfo(SoundComponentGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
- {
- ComponentResult result = noErr;
-
- switch (selector) {
-
- case kVUSelectorSubType:
- ((short*)infoPtr)[0] = globals->peakSampleLeft;
- ((short*)infoPtr)[1] = globals->peakSampleRight;
- break;
-
- default:
- result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
- }
-
- return (result);
- }
-
- /* ==============================================================================
- StopSource
-
- This routine is used to stop sounds that are currently playing. It should
- clear out any internal buffers, reset any compression state information
- and then delegate the call up the chain. We have no state so we just send
- the call up the chain.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentStopSource(SoundComponentGlobalsPtr globals, short count, SoundSource *sources) {
-
- // values are no longer valid
- globals->peakSampleLeft = 0;
- globals->peakSampleRight = 0;
-
- // delegate this call
- return (SoundComponentStopSource(globals->sourceComponent, count, sources));
- }
-
- /* ==============================================================================
- PlaySourceBuffer
-
- This routine is used to start a new sound playing. It should clear out any internal buffers
- but should NOT reset any compression state information, since this could be a
- continuation of a sound that has been broken into pieces. Then the call should be
- delegated up the chain. We have no state so we just send the call up the chain.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentPlaySourceBuffer(SoundComponentGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions) {
-
- // values are no longer valid, starting a new sound
- globals->peakSampleLeft = 0;
- globals->peakSampleRight = 0;
-
- // delegate this call
- return (SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions));
- }
-
- /* ==============================================================================
- SetSource
-
- This routine sets the component we should call to get more data. We must remember
- this component.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentSetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance source) {
- #pragma unused (sourceID)
-
- // remember our source component
- globals->sourceComponent = source;
-
- return (noErr);
- }
-
- /* ==============================================================================
- GetSource
-
- This routine returns the component we call to get more data.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentGetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance *source) {
- #pragma unused (sourceID)
-
- *source = globals->sourceComponent;
- return (noErr);
- }
-
- /* ==============================================================================
- SetOutput
-
- This routine sets the data format our component should output. If we can't output
- the requested format, we should return a pointer to the format we do support,
- and return an error, and the Sound Manager will attempt the conversion for us.
-
- For this component, because we don't modify the data there is no problem
- outputting any format.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentSetOutput(SoundComponentGlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual) {
- #pragma unused (actual, globals, requested)
-
- // no problem outputting anything because we only "look" at the data
- return (noErr);
- }
-
- /* ==============================================================================
- GetSourceData
-
- This routine is called when the Sound Manager wants your component to process
- some more data.
-
- NOTE: This will most often be called at interrupt time.
- ============================================================================== */
-
- static pascal ComponentResult __SoundComponentGetSourceData(SoundComponentGlobalsPtr globals, SoundComponentDataPtr *resultDataPtr) {
- #pragma unused (resultDataPtr)
- ComponentResult result;
-
- // Get some sound data to look at
- result = SoundComponentGetSourceData(globals->sourceComponent, resultDataPtr);
-
- // Sample the buffer to get VU meter data
- VUMeterBuffer(globals, (*resultDataPtr)->buffer, (*resultDataPtr)->sampleCount, (*resultDataPtr)->sampleSize, (*resultDataPtr)->numChannels);
-
- return result;
- }
-
- /* ==============================================================================
- VUMeterBuffer
-
- This implements a simple peak meter.
-
- This code works on the assumtion that the average of a sound wave is 0 (for
- each positive sample there is a coresponding negative sample), therefore we
- only have to find the largest positive sample in the buffer because that will
- be very close to the absolute value of the largest negative sample.
- ============================================================================== */
-
- static void VUMeterBuffer(SoundComponentGlobalsPtr globals, const Byte * const inputBuffer, long samples, short sampleSize, short numChannels) {
- int i;
- short *shortBuffer;
- Byte *charBuffer;
-
- shortBuffer = (short*)inputBuffer;
- charBuffer = (Byte*)inputBuffer;
-
- globals->peakSampleLeft = 0;
- globals->peakSampleRight = 0;
-
- if (sampleSize == 8) {
- if (numChannels == 1) {
- for (i = 0; i < samples; i++) {
- if (*charBuffer > globals->peakSampleLeft)
- globals->peakSampleLeft = *charBuffer;
- charBuffer++;
- }
- } else if (numChannels == 2) {
- for (i = 0; i < samples * 2; i++) {
- if (*charBuffer > globals->peakSampleLeft)
- globals->peakSampleLeft = *charBuffer;
- charBuffer++;
- if (*charBuffer > globals->peakSampleRight)
- globals->peakSampleRight = *charBuffer;
- charBuffer++;
- }
- }
- } else if (sampleSize == 16) {
- if (numChannels == 1) {
- for (i = 0; i < samples; i++) {
- if (*shortBuffer > globals->peakSampleLeft)
- globals->peakSampleLeft = *shortBuffer;
- shortBuffer++;
- }
- } else if (numChannels == 2) {
- for (i = 0; i < samples * 2; i++) {
- if (*shortBuffer > globals->peakSampleLeft)
- globals->peakSampleLeft = *shortBuffer;
- shortBuffer++;
- if (*shortBuffer > globals->peakSampleRight)
- globals->peakSampleRight = *shortBuffer;
- shortBuffer++;
- }
- }
- }
- }
-
-